﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Schema;
using System.Xml.Linq;

namespace TEAA
{
    public class XmlCatalogSystemManager: CatalogSystemManager
    {

        /// <summary>
        /// The file extension to use for database catalog files
        /// </summary>
        private const string catalogFileExtension = ".xml";

        /// <summary>
        /// Schema defined XML elements' name
        /// </summary>
        private const string databaseElement = "database";
        private const string databaseNameAttribute = "databaseName";
        private const string xmlnsAttribute = "xmlns";
        private const string tablesElement = "tables";
        private const string tableElement = "table";
        private const string tableNameAttribute = "tableName";
        private const string tableFileNameAttribute = "tableFileName";
        private const string tableRowLengthAttribute = "rowLength";
        private const string tableStructureElement = "tableStructure";
        private const string tableAttributeElement = "attribute";
        private const string attributeNameAttribute = "attributeName";
        private const string dataTypeAttribute = "dataType";
        private const string isNullAttribute = "isNull";
        private const string lengthAttribute = "length";
        private const string primaryKeyElement = "primaryKey";
        private const string primaryKeyAttributeElement = "primaryKeyAttribute";
        private const string foreignKeysElement = "foreignKeys";
        private const string foreignKeyElement = "foreignKey";
        private const string foreignKeyAttributeElement = "foreignKeyAttribute";
        private const string referenceElement = "reference";
        private const string referenceTableElement = "referenceTable";
        private const string referenceAttributeElement = "referenceAttribute";
        private const string uniqueKeysElement = "uniqueKeys";
        private const string uniqueKeyAttributeElement = "uniqueKeyAttribute";
        private const string indexesElement = "indexes";
        private const string indexElement = "index";
        private const string indexFileNameAttribute = "indexFileName";
        private const string indexTypeAttribute = "indexType";
        private const string isUniqueAttribute = "isUnique";
        private const string keyLengthAttribute = "keyLength";
        private const string indexAttributesElement = "indexAttributes";
        private const string indexAttributeElement = "indexAttribute";

        /// <summary>
        /// Catalog paths and filenames
        /// </summary>
        private string databaseRootLocation = "";
        private string databaseName = "";
        private string catalogLocation = "";
        private string catalogFile = "";
        private string catalogPath = "";

        /// <summary>
        /// Catalog XML loaded to memory
        /// </summary>
        private XDocument catalogXml;

        /// <summary>
        /// Backup save of catalog
        /// </summary>
        private string catalogBackupFilePath;

        /// <summary>
        /// XML namespace
        /// </summary>
        private XNamespace xmlns;

        /// <summary>
        /// XML schema validator
        /// </summary>
        private XmlSchemaValidator xmlSchemaValidator;



        /// <summary>
        /// Constructor that creates validator object
        /// </summary>
        /// <param name="schemaNamespace">Schema namespace</param>
        /// <param name="schemaUri">Schema URI</param>
        public XmlCatalogSystemManager(string schemaNamespace, string schemaUri)
        {
            xmlSchemaValidator = new XmlSchemaValidator(schemaNamespace, schemaUri);
            xmlns = schemaNamespace;
        }


        //////////////////////////////////////////////////////////////////////////////////////////////
        ////////    Catalog XML management
        //////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Load database catalog XML into catalogXml
        /// </summary>
        private void LoadCatalog()
        {
            XDocument tempCatalog = XDocument.Load(catalogPath);
            xmlSchemaValidator.ValidateXDocument(tempCatalog);
            if (xmlSchemaValidator.IsValidXml)
                catalogXml = tempCatalog;
            else
                throw new Exception("Invalid catalog XML, because " + xmlSchemaValidator.ValidationError + "! ");
        }

        /// <summary>
        /// Save database catalog XML from catalogXml
        /// </summary>
        private void SaveCatalog()
        {
            xmlSchemaValidator.ValidateXDocument(catalogXml);
            if (xmlSchemaValidator.IsValidXml)
                catalogXml.Save(catalogPath);
            else
                throw new Exception("Invalid catalog XML, because " + xmlSchemaValidator.ValidationError + "! ");
        }

        /// <summary>
        /// Create database catalog base structure
        /// </summary>
        private void CreateDatabaseCatalog()
        {
            try
            {
                XDocument newDatabaseCatalog = new XDocument();
                XElement database = new XElement(xmlns + databaseElement);
                XAttribute databaseName = new XAttribute(databaseNameAttribute, this.databaseName);
                XElement tables = new XElement(xmlns + tablesElement);

                database.Add(databaseName);
                database.Add(tables);
                newDatabaseCatalog.Add(database);

                newDatabaseCatalog.Save(catalogPath);
            }
            catch (Exception e)
            {
                throw new Exception("Could not create catalog for database \"" + databaseName + "\"! ", e);
            }
        }


        //////////////////////////////////////////////////////////////////////////////////////////////
        ////////    Catalog elements
        //////////////////////////////////////////////////////////////////////////////////////////////


        private XElement GetDatabaseElement()
        {
            XElement database;

            try
            {
                database = catalogXml.Element(xmlns + databaseElement);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + databaseElement + " element! ");
            }

            return database;
        }

        private XAttribute GetDatabaseNameAttribute(XElement database)
        {
            XAttribute databaseName;

            try
            {
                databaseName = database.Attribute(databaseNameAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + databaseNameAttribute + " attribute! ");
            }

            return databaseName;
        }

        private XElement GetTablesElement()
        {
            XElement tables;

            try
            {
                tables = catalogXml.Descendants().
                    Where(e => e.Name == (xmlns + tablesElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + tablesElement + " element! ");
            }

            return tables;
        }

        private IEnumerable<XElement> GetAllTableElements()
        {
            IEnumerable<XElement> tableElements;
            try
            {
                tableElements = catalogXml.Descendants().Where(e => e.Name == (xmlns + tableElement));
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + tableElement + " element! ");
            }
            return tableElements;
        }

        /// <summary>
        /// Get table element with given table name
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>Table element</returns>
        private XElement GetTableElement(string tableName)
        {
            XElement table;
            try
            {
                table = catalogXml.Descendants().
                    Where(e => e.Name == (xmlns + tableElement) && e.Attribute(tableNameAttribute).Value.Equals(tableName)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + tableElement + " element! ");
            }

            return table;
        }

        private XAttribute GetTableNameAttribute(XElement table)
        {
            XAttribute tableNameTableAttribute;

            try
            {
                tableNameTableAttribute = table.Attribute(tableNameAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + tableNameAttribute + " attribute! ");
            }

            return tableNameTableAttribute;
        }

        private XAttribute GetTableFileNameAttribute(XElement table)
        {
            XAttribute tableFileNameTableAttribute;

            try
            {
                tableFileNameTableAttribute = table.Attribute(tableFileNameAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + tableFileNameAttribute + " attribute! ");
            }

            return tableFileNameTableAttribute;
        }

        private XAttribute GetTableRowLengthAttribute(XElement table)
        {
            XAttribute tableRow;

            try
            {
                tableRow = table.Attribute(tableRowLengthAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + tableRowLengthAttribute + " attribute! ");
            }

            return tableRow;
        }

        private XElement GetTableStructureElement(XElement table)
        {
            XElement tableStructure;

            try
            {
                tableStructure = table.Element(xmlns + tableStructureElement);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + tableStructureElement + " element! ");
            }

            return tableStructure;
        }

        private IEnumerable<XElement> GetAllTableAttributeElements(XElement table)
        {
            IEnumerable<XElement> tableAttributeElements;
            try
            {
                tableAttributeElements = table.Descendants().Where(e => e.Name == (xmlns + tableAttributeElement));
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + tableAttributeElement + " element! ");
            }
            return tableAttributeElements;
        }

        private XElement GetTableAttributeElement(XElement table, string attributeName)
        {
            XElement tableAttribute;

            try
            {
                tableAttribute = table.Descendants().
                    Where(e => e.Name == (xmlns + tableAttributeElement) && e.Attribute(attributeNameAttribute).Value.Equals(attributeName)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + tableAttributeElement + " element with " + attributeNameAttribute + " attribute! ");
            }

            return tableAttribute;
        }

        private XAttribute GetTableAttributeAttributeNameAttribute(XElement tableAttribute)
        {
            XAttribute attributeName;

            try
            {
                attributeName = tableAttribute.Attribute(attributeNameAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + attributeNameAttribute + " attribute! ");
            }

            return attributeName;
        }

        private XAttribute GetTableAttributeDataTypeAttribute(XElement tableAttribute)
        {
            XAttribute dataType;

            try
            {
                dataType = tableAttribute.Attribute(dataTypeAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + dataTypeAttribute + " attribute! ");
            }

            return dataType;
        }

        private XAttribute GetTableAttributeIsNullAttribute(XElement tableAttribute)
        {
            XAttribute isNull;

            try
            {
                isNull = tableAttribute.Attribute(isNullAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + isNullAttribute + " attribute! ");
            }

            return isNull;
        }

        private XAttribute GetTableAttributeLengthAttribute(XElement tableAttribute)
        {
            XAttribute length;

            try
            {
                length = tableAttribute.Attribute(lengthAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + lengthAttribute + " attribute! ");
            }

            return length;
        }

        private XElement GetPrimaryKeyElement(XElement table)
        {
            XElement primaryKey;

            try
            {
                primaryKey = table.Descendants().
                    Where(e => e.Name == (xmlns + primaryKeyElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + primaryKeyElement + " element! ");
            }

            return primaryKey;
        }

        private XElement GetPrimaryKeyAttributeElement(XElement table, string attributeName)
        {
            XElement primaryKeyAttribute;

            try
            {
                primaryKeyAttribute = table.Descendants().
                    Where(e => e.Name == (xmlns + primaryKeyAttributeElement) && e.Value.Equals(attributeName)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + primaryKeyAttributeElement + " element! ");
            }

            return primaryKeyAttribute;
        }

        private IEnumerable<XElement> GetAllPrimaryKeyAttributeElements(XElement table)
        {
            IEnumerable<XElement> primaryKeyAttributes;

            try
            {
                primaryKeyAttributes = table.Descendants().
                    Where(e => e.Name == (xmlns + primaryKeyAttributeElement)).
                    Select(e => e);
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + primaryKeyAttributeElement + " element! ");
            }

            return primaryKeyAttributes;
        }

        private XElement GetUniqueKeysElement(XElement table)
        {
            XElement uniqueKeys;

            try
            {
                uniqueKeys = table.Descendants().
                    Where(e => e.Name == (xmlns + uniqueKeysElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + uniqueKeysElement + " element! ");
            }

            return uniqueKeys;
        }

        private XElement GetUniqueKeyAttributeElement(XElement table, string attributeName)
        {
            XElement uniqueKeyAttribute;

            try
            {
                uniqueKeyAttribute = table.Descendants().
                    Where(e => e.Name == (xmlns + uniqueKeyAttributeElement) && e.Value.Equals(attributeName)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + uniqueKeyAttributeElement + " element! ");
            }

            return uniqueKeyAttribute;
        }

        private IEnumerable<XElement> GetAllUniqueKeyAttributeElements(XElement table)
        {
            IEnumerable<XElement> uniqueKeyAttributes;

            try
            {
                uniqueKeyAttributes = table.Descendants().
                    Where(e => e.Name == (xmlns + uniqueKeyAttributeElement)).
                    Select(e => e);
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + uniqueKeyAttributeElement + " element! ");
            }

            return uniqueKeyAttributes;
        }

        private XElement GetForeignKeysElement(XElement table)
        {
            XElement foreignKeys;

            try
            {
                foreignKeys = table.Descendants().
                    Where(e => e.Name == (xmlns + foreignKeysElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + foreignKeysElement + " element! ");
            }

            return foreignKeys;
        }

        private XElement GetForeignKeyElement(XElement table, string attributeName)
        {
            XElement foreignKey;

            try
            {
                foreignKey = table.Descendants().
                    Where(e => e.Name == (xmlns + foreignKeyAttributeElement) && e.Value.Equals(attributeName)).
                    Select(e => e.Parent).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + foreignKeyAttributeElement + " element! ");
            }

            return foreignKey;
        }

        private XElement GetForeignKeyAttributeElement(XElement foreignKey)
        {
            XElement foreignKeyAttribute;

            try
            {
                foreignKeyAttribute = foreignKey.Descendants().
                    Where(e => e.Name == (xmlns + foreignKeyAttributeElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + foreignKeyAttributeElement + " element of FOREIGN KEY! ");
            }

            return foreignKeyAttribute;
        }

        private XElement GetForeignKeyReferenceElement(XElement foreignKey)
        {
            XElement reference;

            try
            {
                reference = foreignKey.Descendants().
                    Where(e => e.Name == (xmlns + referenceElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + referenceElement + " element of FOREIGN KEY! ");
            }

            return reference;
        }

        private XElement GetForeignKeyReferenceTableElement(XElement foreignKey)
        {
            XElement referenceTable;

            try
            {
                referenceTable = foreignKey.Descendants().
                    Where(e => e.Name == (xmlns + referenceTableElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + referenceTableElement + " element of FOREIGN KEY! ");
            }

            return referenceTable;
        }

        private XElement GetForeignKeyReferenceAttributeElement(XElement foreignKey)
        {
            XElement referenceAttribute;

            try
            {
                referenceAttribute = foreignKey.Descendants().
                    Where(e => e.Name == (xmlns + referenceAttributeElement)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + referenceAttributeElement + " element of FOREIGN KEY! ");
            }

            return referenceAttribute;
        }

        private IEnumerable<XElement> GetAllForeignKeyElements(XElement table)
        {
            IEnumerable<XElement> foreignKeys;

            try
            {
                foreignKeys = table.Descendants().
                    Where(e => e.Name == (xmlns + foreignKeyElement));
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + foreignKeyElement + " element! ");
            }

            return foreignKeys;
        }

        private IEnumerable<XElement> GetAllForeignKeyElementsReferencingOnTable(string tableName)
        {
            IEnumerable<XElement> foreignKeys;

            try
            {
                foreignKeys = catalogXml.Descendants().
                        Where(e => e.Name == (xmlns + foreignKeyElement) && e.Element(xmlns + referenceElement).
                            Element(xmlns + referenceTableElement).Value.Equals(tableName)).
                        Select(e => e);
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + foreignKeyElement + " element! ");
            }

            return foreignKeys;
        }

        private IEnumerable<XElement> GetAllForeignKeyElementsReferencingOnAttribute(string tableName, string attributeName)
        {
            IEnumerable<XElement> foreignKeys;

            try
            {
                foreignKeys = catalogXml.Descendants().
                        Where(e => e.Name == (xmlns + foreignKeyElement) && e.Element(xmlns + referenceElement).
                            Element(xmlns + referenceTableElement).Value.Equals(tableName) &&
                            e.Element(xmlns + referenceElement).Element(xmlns + referenceAttributeElement).Value.Equals(attributeName)).
                        Select(e => e);
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + foreignKeyElement + " element! ");
            }

            return foreignKeys;
        }

        private XElement GetIndexesElement(XElement table)
        {
            XElement indexes;

            try
            {
                indexes = table.Element(xmlns + indexesElement);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + indexesElement + " element! ");
            }

            return indexes;
        }

        private XElement GetIndexElementByFileName(XElement table, string indexFileName)
        {
            XElement index;

            try
            {
                index = table.Descendants().
                    Where(e => e.Name == (xmlns + indexElement) && e.Attribute(indexFileNameAttribute).Value.Equals(indexFileName)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + indexElement + " element with " + indexFileNameAttribute + " attribute! ");
            }

            return index;
        }

        private XElement GetIndexElementByAttribute(XElement table, string indexAttribute)
        {
            XElement index;

            try
            {
                index = table.Descendants().
                    Where(e => e.Name == (xmlns + indexElement) && e.Element(xmlns + indexAttributesElement).
                        Element(xmlns + indexAttributeElement).Value.Equals(indexAttribute)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + indexAttributeElement + " element! ");
            }

            return index;
        }

        private XAttribute GetIndexFileNameAttribute(XElement index)
        {
            XAttribute indexFileName;

            try
            {
                indexFileName = index.Attribute(indexFileNameAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + indexFileNameAttribute + " attribute! ");
            }

            return indexFileName;
        }

        private XElement GetIndexAttributeElement(XElement index, string attributeName)
        {
            XElement indexAttribute;

            try
            {
                indexAttribute = index.Descendants().
                    Where(e => e.Name == (xmlns + indexAttributeElement) && e.Value.Equals(attributeName)).
                    Select(e => e).Single();
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + indexAttributeElement + " element! ");
            }

            return indexAttribute;
        }

        private XAttribute GetIndexTypeAttribute(XElement index)
        {
            XAttribute indexType;

            try
            {
                indexType = index.Attribute(indexTypeAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + indexTypeAttribute + " attribute! ");
            }

            return indexType;
        }

        private XAttribute GetIndexIsUniqueAttribute(XElement index)
        {
            XAttribute indexType;

            try
            {
                indexType = index.Attribute(isUniqueAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + isUniqueAttribute + " attribute! ");
            }

            return indexType;
        }

        private XAttribute GetIndexKeyLengthAttribute(XElement index)
        {
            XAttribute indexType;

            try
            {
                indexType = index.Attribute(keyLengthAttribute);
            }
            catch (Exception)
            {
                throw new Exception("Could not find " + keyLengthAttribute + " attribute! ");
            }

            return indexType;
        }

        private IEnumerable<XElement> GetIndexAttributes(XElement index)
        {
            IEnumerable<XElement> indexAttributes;

            try
            {
                indexAttributes = index.Descendants().
                        Where(e => e.Name == (xmlns + indexAttributeElement));
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + indexAttributeElement + " element! ");
            }

            return indexAttributes;
        }

        private IEnumerable<XElement> GetAllIndexElementsReferencingOnAttribute(XElement table, string attributeName)
        {
            IEnumerable<XElement> indexes;

            try
            {
                indexes = table.Descendants().
                        Where(e => e.Name == (xmlns + indexElement) && e.Element(xmlns + indexAttributesElement).
                            Element(xmlns + indexAttributeElement).Value.Equals(attributeName)).
                        Select(e => e);
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + indexElement + " element! ");
            }

            return indexes;
        }

        private IEnumerable<XElement> GetAllIndexElements(XElement table)
        {
            IEnumerable<XElement> indexes;

            try
            {
                indexes = table.Descendants().
                        Where(e => e.Name == (xmlns + indexElement));
            }
            catch (Exception)
            {
                throw new Exception("Wrong catalog structure for " + indexElement + " element! ");
            }

            return indexes;
        }



        //////////////////////////////////////////////////////////////////////////////////////////////
        ////////    Catalog operations
        //////////////////////////////////////////////////////////////////////////////////////////////


        /// <summary>
        /// Make a backup on database catalog
        /// </summary>
        public override void BackupDatabaseCatalog()
        {
            catalogBackupFilePath = catalogLocation + Path.DirectorySeparatorChar + databaseName + ".backup";
            catalogXml.Save(catalogBackupFilePath);
        }

        /// <summary>
        /// Restore backed up database catalog
        /// </summary>
        public override void RestoreDatabaseCatalog()
        {
            if (catalogBackupFilePath != null)
            {
                catalogXml = XDocument.Load(catalogBackupFilePath);
                SaveCatalog();
                catalogBackupFilePath = null;
            }
            else
                throw new Exception("No catalog backup was found to restore database \"" + databaseName + "\"! ");
        }

        /// <summary>
        /// Initialize a database catalog, if catalog file exists it is loaded, 
        /// if not a new catalog file is created
        /// </summary>
        /// <param name="databaseRootLocation">Root location of databases</param>
        /// <param name="databaseName">Name of the database</param>
        public override void InitializeDatabaseCatalog(string databaseRootLocation, string databaseName)
        {
            this.databaseRootLocation = databaseRootLocation;
            this.databaseName = databaseName;
            catalogLocation = databaseRootLocation + Path.DirectorySeparatorChar + databaseName;
            catalogFile = databaseName + catalogFileExtension;

            catalogPath = catalogLocation + Path.DirectorySeparatorChar + catalogFile;
            try
            {
                if (!File.Exists(catalogPath))
                {
                    CreateDatabaseCatalog();
                }
                LoadCatalog();
            }
            catch (Exception e)
            {
                throw new Exception("Could not initialize catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Reload database catalog to catalogXml
        /// </summary>
        public override void ReloadDatabaseCatalog()
        {
            try
            {
                LoadCatalog();
            }
            catch (Exception e)
            {
                throw new Exception("Could not reload catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Alter database
        /// </summary>
        /// <param name="newDatabaseName">New database name</param>
        public override void AlterDatabase(string newDatabaseName)
        {
            try
            {
                if (newDatabaseName.Equals("") || newDatabaseName == null)
                    throw new Exception("Invalid new database name! ");

                string newCatalogFile = newDatabaseName + catalogFileExtension;
                string tempCatalogPath = databaseRootLocation + Path.DirectorySeparatorChar + newDatabaseName +
                    Path.DirectorySeparatorChar + catalogFile;
                FileManager.RenameFile(tempCatalogPath, newCatalogFile);
                InitializeDatabaseCatalog(databaseRootLocation, newDatabaseName);

                XElement database;
                XAttribute databaseName;

                database = GetDatabaseElement();
                databaseName = GetDatabaseNameAttribute(database);

                databaseName.SetValue(newDatabaseName);
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not rename database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Get database name
        /// </summary>
        /// <returns>Name of the database</returns>
        public override string GetDatabaseName()
        {
            try
            {
                XElement database = GetDatabaseElement();
                return GetDatabaseNameAttribute(database).Value;
            }
            catch (Exception e)
            {
                throw new Exception("Could not get database name from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get tables from catalog
        /// </summary>
        /// <returns>List of table names</returns>
        public override List<string> GetTables()
        {
            List<string> tables = new List<string>();
            try
            {
                LoadCatalog();
                foreach (XElement table in GetAllTableElements())
                {
                    string tableName = GetTableNameAttribute(table).Value;
                    tables.Add(tableName);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Could not get tables of database \"" + databaseName + "\" in catalog! ", e);
            }
            return tables;
        }

        /// <summary>
        /// Create new table in database catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="tableFileName">Name of the table file</param>
        public override void CreateTable(string tableName, string tableFileName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (tableFileName.Equals("") || tableFileName == null)
                    throw new Exception("Invalid table file name! ");

                XElement newTable = new XElement(xmlns + tableElement);
                XAttribute newTableName = new XAttribute(tableNameAttribute, tableName);
                XAttribute newTableFileName = new XAttribute(tableFileNameAttribute, tableFileName);
                XAttribute newTableRowLength = new XAttribute(tableRowLengthAttribute, 0);
                XElement tableStructure = new XElement(xmlns + tableStructureElement);
                XElement primaryKey = new XElement(xmlns + primaryKeyElement);
                XElement foreignKeys = new XElement(xmlns + foreignKeysElement);
                XElement uniqueKeys = new XElement(xmlns + uniqueKeysElement);
                XElement indexes = new XElement(xmlns + indexesElement);

                newTable.Add(newTableName);
                newTable.Add(newTableFileName);
                newTable.Add(newTableRowLength);
                newTable.Add(tableStructure);
                newTable.Add(primaryKey);
                newTable.Add(foreignKeys);
                newTable.Add(uniqueKeys);
                newTable.Add(indexes);

                XElement tables;

                tables = GetTablesElement();
                if (ExistsTable(tableName))
                    throw new Exception("Table already exists with this name! ");
                tables.Add(newTable);
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not add table \"" + tableName + "\" to database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Alter an existing table in catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="newTableName">New name of the table</param>
        /// <param name="newTableFileName">New file of the table</param>
        public override void AlterTable(string tableName, string newTableName, string newTableFileName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (newTableName.Equals("") || newTableName == null)
                    throw new Exception("Invalid new table name! ");
                if (newTableFileName.Equals("") || newTableFileName == null)
                    throw new Exception("Invalid new table file name! ");

                XElement table;
                XAttribute tableNameAttribute;
                XAttribute tableFileNameAttribute;

                table = GetTableElement(tableName);
                tableNameAttribute = GetTableNameAttribute(table);
                tableFileNameAttribute = GetTableFileNameAttribute(table);

                if (ExistsTable(newTableName))
                    throw new Exception("Table already exists with this name! ");
                tableNameAttribute.Value = newTableName;
                tableFileNameAttribute.Value = newTableFileName;
                CascadeAlterTableChanges(tableName, newTableName);
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not alter table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Cascade alter table changes to references on the table
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="newTableName">New name of the table</param>
        private void CascadeAlterTableChanges(string tableName, string newTableName)
        {
            try
            {
                IEnumerable<XElement> foreignKeys;

                foreignKeys = GetAllForeignKeyElementsReferencingOnTable(tableName);
                foreach (XElement foreignKey in foreignKeys)
                {
                    GetForeignKeyReferenceTableElement(foreignKey).Value = newTableName;
                }
            }
            catch (Exception)
            {
                throw new Exception("Could not cascade changes of table to FOREIGN KEY constraints! ");
            }
        }

        /// <summary>
        /// Drop an existing table from catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        public override void DropTable(string tableName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");

                XElement table;

                table = GetTableElement(tableName);

                table.Remove();
                CheckTableReferences(tableName);
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not drop table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Check if table exists
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>True if exists, else false</returns>
        public override bool ExistsTable(string tableName)
        {
            bool tableExists = true;

            try
            {
                GetTableElement(tableName);
            }
            catch (Exception)
            {
                tableExists = false;
            }

            return tableExists;
        }

        /// <summary>
        /// Check if table is referenced by FOREIGN KEY constraints
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        private void CheckTableReferences(string tableName)
        {
            IEnumerable<XElement> foreignKeys;

            foreignKeys = GetAllForeignKeyElementsReferencingOnTable(tableName);

            if (foreignKeys.Count() > 0)
                throw new Exception("Table is referenced by one or more FOREIGN KEY constraints! ");
        }

        /// <summary>
        /// Get table file name
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>Table file name</returns>
        public override string GetTableFileName(string tableName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                return GetTableFileNameAttribute(table).Value;
            }
            catch (Exception e)
            {
                throw new Exception("Could not get table filename of table \"" + tableName +
                    "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get table row length
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>Table row length</returns>
        public override int GetTableRowLength(string tableName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                return Int32.Parse(GetTableRowLengthAttribute(table).Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get table row length of table \"" + tableName +
                    "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get table attributes from catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>List of table attributes</returns>
        public override List<string> GetTableAttributes(string tableName)
        {
            List<string> tableAttributes = new List<string>();
            try
            {
                LoadCatalog();
                XElement table = GetTableElement(tableName);
                foreach (XElement attribute in GetAllTableAttributeElements(table))
                {
                    string attributeName = GetTableAttributeAttributeNameAttribute(attribute).Value;
                    tableAttributes.Add(attributeName);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Could not get attributes in table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
            return tableAttributes;
        }

        /// <summary>
        /// Create an attribute to table in catalog (column)
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <param name="dataType">Data type</param>
        /// <param name="isNull">Possible attribute to get null value</param>
        /// <param name="length">Length of the table attribute</param>
        public override void CreateTableAttribute(string tableName, string attributeName, DataType dataType, bool isNull, int length)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table atrribute name! ");
                if (length <= 0)
                    throw new Exception("Invalid table attribute length! ");

                XElement tableAttribute = new XElement(xmlns + tableAttributeElement);
                XAttribute tableAttributeName = new XAttribute(attributeNameAttribute, attributeName);
                XAttribute attributeDataType = new XAttribute(dataTypeAttribute, dataType);
                XAttribute attributeIsNull = new XAttribute(isNullAttribute, isNull);
                XAttribute attributeLength = new XAttribute(lengthAttribute, length);

                tableAttribute.Add(tableAttributeName);
                tableAttribute.Add(attributeDataType);
                tableAttribute.Add(attributeIsNull);
                tableAttribute.Add(attributeLength);

                XElement table;
                XElement tableStructure;
                XAttribute tableRowLength;

                table = GetTableElement(tableName);
                tableStructure = GetTableStructureElement(table);
                tableRowLength = GetTableRowLengthAttribute(table);

                if (ExistsTableAttribute(tableName, attributeName))
                    throw new Exception("Table attribute already exists with this name! ");
                tableStructure.Add(tableAttribute);
                int rowLength = Int32.Parse(tableRowLength.Value);
                rowLength += length;
                tableRowLength.Value = rowLength.ToString();
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not add table attribute (column) to table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Alter existing table attribute in catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <param name="newAttributeName">New name of the attribute</param>
        /// <param name="newDataType">New data type</param>
        /// <param name="newIsNull">New possible attribute to get null value</param>
        /// <param name="newLength">New length of attribute</param>
        public override void AlterTableAttribute(string tableName, string attributeName, string newAttributeName,
            DataType newDataType, bool newIsNull, int newLength)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");
                if (newAttributeName.Equals("") || newAttributeName == null)
                    throw new Exception("Invalid new table attribute name! ");
                if (newLength <= 0)
                    throw new Exception("Invalid new table attribute length! ");

                XElement table;
                XElement tableAttribute;
                XAttribute tableAttributeName;
                XAttribute tableAttributeDataType;
                XAttribute tableAttributeIsnull;
                XAttribute tableAttributeLength;
                XAttribute tableRowLength;

                table = GetTableElement(tableName);
                tableAttribute = GetTableAttributeElement(table, attributeName);
                tableAttributeName = GetTableAttributeAttributeNameAttribute(tableAttribute);
                tableAttributeDataType = GetTableAttributeDataTypeAttribute(tableAttribute);
                tableAttributeIsnull = GetTableAttributeIsNullAttribute(tableAttribute);
                tableAttributeLength = GetTableAttributeLengthAttribute(tableAttribute);
                tableRowLength = GetTableRowLengthAttribute(table);

                if (!tableAttributeName.Value.Equals(newAttributeName))
                {
                    if (ExistsTableAttribute(tableName, newAttributeName))
                        throw new Exception("Table attribute already exists with this name! ");
                    CascadeTableAttributeChanges(tableName, attributeName, newAttributeName);
                    tableAttributeName.Value = newAttributeName;
                }
                if (!Enum.Parse(typeof(DataType), tableAttributeDataType.Value).Equals(newDataType))
                {
                    if (IsForeignKey(tableName, attributeName) || IsForeignKeyReference(tableName, attributeName))
                        throw new Exception("Invalid data type change for table attribute that is part of a FOREIGN KEY constraint! ");
                    tableAttributeDataType.Value = newDataType.ToString();
                }
                if (!(Boolean.Parse(tableAttributeIsnull.Value) == newIsNull))
                {
                    if (IsPrimaryKey(tableName, attributeName))
                        throw new Exception("Table attribute cannot take NULL values as PRIMARY KEY! ");
                    tableAttributeIsnull.Value = newIsNull.ToString().ToLower();
                }
                if (!(Int32.Parse(tableAttributeLength.Value) == newLength))
                {
                    if (IsForeignKey(tableName, attributeName) || IsForeignKeyReference(tableName, attributeName))
                        throw new Exception("Invalid length change for table attribute that is part of a FOREIGN KEY constraint! ");
                    tableAttributeLength.Value = newLength.ToString();
                    int rowLength = Int32.Parse(tableRowLength.Value);
                    int attributeLength = Int32.Parse(tableAttributeLength.Value);
                    rowLength -= attributeLength;
                    rowLength += newLength;
                    tableRowLength.Value = rowLength.ToString();
                }
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not alter table attribute (column) in table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Cascade table attribute changes to references on table attribute
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <param name="newAttributeName">New name of the attribute</param>
        private void CascadeTableAttributeChanges(string tableName, string attributeName, string newAttributeName)
        {
            try
            {
                XElement table;

                table = GetTableElement(tableName);

                if (IsPrimaryKey(tableName, attributeName))
                    GetPrimaryKeyAttributeElement(table, attributeName).Value = newAttributeName;
                if (IsUniqueKey(tableName, attributeName))
                    GetUniqueKeyAttributeElement(table, attributeName).Value = newAttributeName;
                if (IsForeignKey(tableName, attributeName))
                {
                    XElement foreignKey = GetForeignKeyElement(table, attributeName);
                    GetForeignKeyAttributeElement(foreignKey).Value = newAttributeName;
                }
                if (IsForeignKeyReference(tableName, attributeName))
                {
                    IEnumerable<XElement> foreignKeys = GetAllForeignKeyElementsReferencingOnAttribute(tableName, attributeName);
                    foreach (XElement foreignKey in foreignKeys)
                        GetForeignKeyReferenceAttributeElement(foreignKey).Value = newAttributeName;
                }
                if (HasIndex(tableName, attributeName))
                {
                    IEnumerable<XElement> indexes = GetAllIndexElementsReferencingOnAttribute(table, attributeName);
                    foreach (XElement index in indexes)
                        GetIndexAttributeElement(index, attributeName).Value = newAttributeName;
                }
            }
            catch (Exception)
            {
                throw new Exception("Could not cascade changes to PRIMARY KEY, UNIQUE KEY, FOREIGN KEY constraints or indexes referencing on the table attribute! ");
            }
        }

        /// <summary>
        /// Drop an existing table attribute from catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        public override void DropTableAttribute(string tableName, string attributeName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");

                XElement table;
                XElement tableAttribute;
                XAttribute tableAttributeLength;
                XAttribute tableRowLength;

                table = GetTableElement(tableName);
                tableAttribute = GetTableAttributeElement(table, attributeName);
                tableAttributeLength = GetTableAttributeLengthAttribute(tableAttribute);
                tableRowLength = GetTableRowLengthAttribute(table);

                if (IsForeignKeyReference(tableName, attributeName))
                    throw new Exception("Table attribute is referenced by a FOREIGN KEY constraint! ");
                if (IsPrimaryKey(tableName, attributeName))
                    UnsetPrimaryKeyAttribute(tableName, attributeName);
                if (IsUniqueKey(tableName, attributeName))
                    UnsetUniqueKeyAttribute(tableName, attributeName);
                if (HasIndex(tableName, attributeName))
                {
                    IEnumerable<XElement> indexes = GetAllIndexElementsReferencingOnAttribute(table, attributeName);
                    string[] indexFileNames = new string[indexes.Count()];
                    int i = 0;
                    foreach (XElement index in indexes)
                    {
                        string indexFileName = GetIndexFileNameAttribute(index).Value;
                        indexFileNames[i++] = indexFileName;
                    }
                    foreach (string indexFileName in indexFileNames)
                        DropIndex(tableName, indexFileName);
                }

                int rowLength = Int32.Parse(tableRowLength.Value);
                int attributeLength = Int32.Parse(tableAttributeLength.Value);
                rowLength -= attributeLength;
                tableRowLength.Value = rowLength.ToString();
                tableAttribute.Remove();
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not drop table attribute (column) in table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Check if table attribute exists
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>True if exists, else false</returns>
        public override bool ExistsTableAttribute(string tableName, string attributeName)
        {
            XElement table;
            bool tableAttributeExists = true;

            table = GetTableElement(tableName);
            try
            {
                GetTableAttributeElement(table, attributeName);
            }
            catch (Exception)
            {
                tableAttributeExists = false;
            }

            return tableAttributeExists;
        }

        /// <summary>
        /// Get attribute data type
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>Type of data</returns>
        public override DataType GetTableAttributeDataType(string tableName, string attributeName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement tableAttribute = GetTableAttributeElement(table, attributeName);
                return (DataType)Enum.Parse(typeof(DataType), GetTableAttributeDataTypeAttribute(tableAttribute).Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get data type of attribute \"" + attributeName +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Can table attribute be null
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>True if attribute can be null, else false</returns>
        public override bool IsTableAttributeIsNull(string tableName, string attributeName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement tableAttribute = GetTableAttributeElement(table, attributeName);
                return Boolean.Parse(GetTableAttributeIsNullAttribute(tableAttribute).Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get if can be null attribute \"" + attributeName +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get table attribute length
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>Length of the table attribute</returns>
        public override int GetTableAttributeLength(string tableName, string attributeName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement tableAttribute = GetTableAttributeElement(table, attributeName);
                return Int32.Parse(GetTableAttributeLengthAttribute(tableAttribute).Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get length of attribute \"" + attributeName +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get primary key table attributes
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>List of primary key table attributes</returns>
        public override List<string> GetPrimaryKeyAttributes(string tableName)
        {
            List<string> primaryKeyAttributes = new List<string>();
            try
            {
                LoadCatalog();
                XElement table = GetTableElement(tableName);
                foreach (XElement primaryKeyAttribute in GetAllPrimaryKeyAttributeElements(table))
                {
                    string primaryKey = primaryKeyAttribute.Value;
                    primaryKeyAttributes.Add(primaryKey);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Could not get PRIMARY KEY attributes in table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
            return primaryKeyAttributes;
        }

        /// <summary>
        /// Set table attribute as primary key in catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the table attribute</param>
        public override void SetPrimaryKeyAttribute(string tableName, string attributeName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");

                XElement primaryKeyAttribute = new XElement(xmlns + primaryKeyAttributeElement, attributeName);

                XElement table;
                XElement tableAttribute;
                XElement primaryKey;

                table = GetTableElement(tableName);
                tableAttribute = GetTableAttributeElement(table, attributeName);
                primaryKey = GetPrimaryKeyElement(table);

                bool isNullValue = Boolean.Parse(GetTableAttributeIsNullAttribute(tableAttribute).Value);
                if (isNullValue == true)
                    throw new Exception("Table attribute cannot take NULL values as PRIMARY KEY! ");
                if (IsUniqueKey(tableName, attributeName))
                    throw new Exception("Table attribute is already set as UNIQUE KEY! ");
                if (IsForeignKey(tableName, attributeName))
                    throw new Exception("Table attribute is already set as FOREIGN KEY! ");
                if (!IsPrimaryKey(tableName, attributeName))
                    primaryKey.Add(primaryKeyAttribute);
                else
                    throw new Exception("Table attribute is already set as PRIMARY KEY! ");
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not set as PRIMARY KEY table attribute (column) \"" + attributeName + "\" in table \"" +
                    tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Unset table attribute as a primary key in catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        public override void UnsetPrimaryKeyAttribute(string tableName, string attributeName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");

                XElement table;
                XElement primaryKeyAttribute;

                table = GetTableElement(tableName);
                primaryKeyAttribute = GetPrimaryKeyAttributeElement(table, attributeName);

                if (IsForeignKeyReference(tableName, attributeName))
                    throw new Exception("Table attribute is referenced by a FOPREIGN KEY of a table! ");
                primaryKeyAttribute.Remove();
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not unset as PRIMARY KEY table attribute (column) \"" + attributeName + "\" in table \"" +
                    tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Check if attribute is primary key
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>True if is primary key, else false</returns>
        public override bool IsPrimaryKey(string tableName, string attributeName)
        {
            XElement table;
            bool isPrimaryKey = true;

            table = GetTableElement(tableName);
            try
            {
                GetPrimaryKeyAttributeElement(table, attributeName);
            }
            catch (Exception)
            {
                isPrimaryKey = false;
            }

            return isPrimaryKey;
        }

        /// <summary>
        /// Get unique key table attributes
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>List of unique key table attributes</returns>
        public override List<string> GetUniqueKeyAttributes(string tableName)
        {
            List<string> uniqueKeyAttributes = new List<string>();
            try
            {
                LoadCatalog();
                XElement table = GetTableElement(tableName);
                foreach (XElement uniqueKeyAttribute in GetAllUniqueKeyAttributeElements(table))
                {
                    string uniqueKey = uniqueKeyAttribute.Value;
                    uniqueKeyAttributes.Add(uniqueKey);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Could not get UNIQUE KEY attributes in table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
            return uniqueKeyAttributes;
        }

        /// <summary>
        /// Set table attribute as a unique key in catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        public override void SetUniqueKeyAttribute(string tableName, string attributeName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");

                XElement uniqueKeyAttribute = new XElement(xmlns + uniqueKeyAttributeElement, attributeName);

                XElement table;
                XElement uniqueKeys;

                table = GetTableElement(tableName);
                GetTableAttributeElement(table, attributeName);
                uniqueKeys = GetUniqueKeysElement(table);

                if (IsPrimaryKey(tableName, attributeName))
                    throw new Exception("Table attribute is already set as PRIMARY KEY! ");
                if (IsForeignKey(tableName, attributeName))
                    throw new Exception("Table attribute is already set as FOREIGN KEY! ");
                if (!IsUniqueKey(tableName, attributeName))
                    uniqueKeys.Add(uniqueKeyAttribute);
                else
                    throw new Exception("Table attribute is already set as UNIQUE KEY! ");
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not set as UNIQUE KEY table attribute (column) \"" + attributeName + "\" in table \"" +
                    tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Unset table attribute as a unique key in catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        public override void UnsetUniqueKeyAttribute(string tableName, string attributeName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");

                XElement table;
                XElement uniqueKeyAttribute;

                table = GetTableElement(tableName);
                uniqueKeyAttribute = GetUniqueKeyAttributeElement(table, attributeName);

                if (IsForeignKeyReference(tableName, attributeName))
                    throw new Exception("Table attribute is referenced by a FOPREIGN KEY of a table! ");
                uniqueKeyAttribute.Remove();
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not unset as UNIQUE KEY table attribute (column) \"" + attributeName + "\" in table \"" +
                    tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Check if attribute is unique key
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>True if is unique key, else false</returns>
        public override bool IsUniqueKey(string tableName, string attributeName)
        {
            XElement table;
            bool isUniqueKey = true;

            table = GetTableElement(tableName);
            try
            {
                GetUniqueKeyAttributeElement(table, attributeName);
            }
            catch (Exception)
            {
                isUniqueKey = false;
            }

            return isUniqueKey;
        }

        /// <summary>
        /// Get foreign key table attributes
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>List of foreign key table attributes</returns>
        public override List<string> GetForeignKeyAttributes(string tableName)
        {
            List<string> foreignKeyAttributes = new List<string>();
            try
            {
                LoadCatalog();
                XElement table = GetTableElement(tableName);
                foreach (XElement foreignKeyAttribute in GetAllForeignKeyElements(table))
                {
                    string foreignKey = GetForeignKeyAttributeElement(foreignKeyAttribute).Value;
                    foreignKeyAttributes.Add(foreignKey);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Could not get FOREIGN KEY attributes in table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
            return foreignKeyAttributes;
        }

        /// <summary>
        /// Set table attribute as a foreign key in catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <param name="referenceTableName">Name of the referenced table</param>
        /// <param name="referenceAttributeName">Name of the referenced attribute</param>
        public override void SetForeignKeyAttribute(string tableName, string attributeName,
            string referenceTableName, string referenceAttributeName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");
                if (referenceTableName.Equals("") || referenceTableName == null)
                    throw new Exception("Invalid reference table name! ");
                if (referenceAttributeName.Equals("") || referenceAttributeName == null)
                    throw new Exception("Invalid reference table attribute name! ");

                XElement foreignKey = new XElement(xmlns + foreignKeyElement);
                XElement foreignKeyAttribute = new XElement(xmlns + foreignKeyAttributeElement, attributeName);
                XElement reference = new XElement(xmlns + referenceElement);
                XElement referenceTable = new XElement(xmlns + referenceTableElement, referenceTableName);
                XElement referenceAttribute = new XElement(xmlns + referenceAttributeElement, referenceAttributeName);

                foreignKey.Add(foreignKeyAttribute);
                foreignKey.Add(reference);
                reference.Add(referenceTable);
                reference.Add(referenceAttribute);

                XElement table;
                XElement tableAttribute;
                XElement referredTable;
                XElement referredTableAttribute;
                XElement foreignKeys;

                table = GetTableElement(tableName);
                tableAttribute = GetTableAttributeElement(table, attributeName);
                referredTable = GetTableElement(referenceTableName);
                referredTableAttribute = GetTableAttributeElement(referredTable, referenceAttributeName);
                foreignKeys = GetForeignKeysElement(table);

                if (!IsPrimaryKey(referenceTableName, referenceAttributeName) && !IsUniqueKey(referenceTableName, referenceAttributeName))
                    throw new Exception("Referenced table attribute is not a PRIMARY KEY or a UNIQUE KEY in the table! ");
                if (IsForeignKey(tableName, attributeName))
                    throw new Exception("Table attribute is already a FOREIGN KEY in the table! ");
                DataType tableAttributeDataTypeValue = (DataType)Enum.Parse(typeof(DataType), GetTableAttributeDataTypeAttribute(tableAttribute).Value);
                DataType referredTableAttributeDataTypeValue = (DataType)Enum.Parse(typeof(DataType), GetTableAttributeDataTypeAttribute(referredTableAttribute).Value);
                int tableAttributeLengthValue = Int32.Parse(GetTableAttributeLengthAttribute(tableAttribute).Value);
                int referredTableAttributeLengthValue = Int32.Parse(GetTableAttributeLengthAttribute(referredTableAttribute).Value);
                if (tableAttributeDataTypeValue.Equals(referredTableAttributeDataTypeValue) && (tableAttributeLengthValue == referredTableAttributeLengthValue))
                {
                    foreignKeys.Add(foreignKey);
                }
                else
                    throw new Exception("Attributes are of different types or length! ");
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not set as FOREIGN KEY table attribute (column) \"" + attributeName + "\" in table \"" +
                    tableName + "\" referencing to table attribute (column) \"" + referenceAttributeName + "\" in table \"" +
                    referenceTableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Unset table attribute as a foreign key
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        public override void UnsetForeignKeyAttribute(string tableName, string attributeName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (attributeName.Equals("") || attributeName == null)
                    throw new Exception("Invalid table attribute name! ");

                XElement table;
                XElement foreignKey;

                table = GetTableElement(tableName);
                foreignKey = GetForeignKeyElement(table, attributeName);

                foreignKey.Remove();
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not unset as FOREIGN KEY table attribute (column) \"" + attributeName + "\" in table \"" +
                    tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Check if attribute is foreign key
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>True if is foreign key, else false</returns>
        public override bool IsForeignKey(string tableName, string attributeName)
        {
            XElement table;
            bool isForeignKey = true;

            table = GetTableElement(tableName);
            try
            {
                GetForeignKeyElement(table, attributeName);
            }
            catch (Exception)
            {
                isForeignKey = false;
            }

            return isForeignKey;
        }

        /// <summary>
        /// Check if attribute is a foreign key reference
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>True if is foreign key reference, else false</returns>
        public override bool IsForeignKeyReference(string tableName, string attributeName)
        {
            bool isForeignKeyReference = true;

            try
            {
                int count = GetAllForeignKeyElementsReferencingOnAttribute(tableName, attributeName).Count();
                if (count <= 0)
                    isForeignKeyReference = false;
            }
            catch (Exception)
            {
                isForeignKeyReference = false;
            }

            return isForeignKeyReference;
        }

        /// <summary>
        /// Get foreign key reference table
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="foreignKeyAttribute">Name of the attribute</param>
        /// <returns>Reference table name</returns>
        public override string GetForeignKeyReferenceTable(string tableName, string foreignKeyAttribute)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement foreignKey = GetForeignKeyElement(table, foreignKeyAttribute);
                XElement referenceTable = GetForeignKeyReferenceTableElement(foreignKey);
                return referenceTable.Value;
            }
            catch (Exception e)
            {
                throw new Exception("Could not get reference table of FOREIGN KEY attribute \"" + foreignKeyAttribute +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get foreign key reference attribute
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="foreignKeyAttribute">Name of the attribute</param>
        /// <returns>Reference attribute name</returns>
        public override string GetForeignKeyReferenceTableAttribute(string tableName, string foreignKeyAttribute)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement foreignKey = GetForeignKeyElement(table, foreignKeyAttribute);
                XElement referenceTableAttribute = GetForeignKeyReferenceAttributeElement(foreignKey);
                return referenceTableAttribute.Value;
            }
            catch (Exception e)
            {
                throw new Exception("Could not get reference attribute of FOREIGN KEY attribute \"" + foreignKeyAttribute +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get indexes of table
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <returns>Index names</returns>
        public override List<string> GetIndexes(string tableName)
        {
            List<string> indexes = new List<string>();
            try
            {
                LoadCatalog();
                XElement table = GetTableElement(tableName);
                foreach (XElement index in GetAllIndexElements(table))
                {
                    string indexName = GetIndexFileNameAttribute(index).Value;
                    indexes.Add(indexName);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Could not get indexes of table \"" + tableName + "\" of database \"" + databaseName + "\" in catalog! ", e);
            }
            return indexes;
        }

        /// <summary>
        /// Create index to a table
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeNames">Names of the attributes</param>
        /// <param name="indexFileName">Name of the index file</param>
        /// <param name="indexType">Type of the index</param>
        /// <param name="isUnique">Index is unique</param>
        public override void CreateIndex(string tableName, string[] attributeNames, string indexFileName,
            IndexType indexType, bool isUnique)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (indexFileName.Equals("") || indexFileName == null)
                    throw new Exception("Invalid index file name! ");

                int keyLength = 0;
                XElement table;
                XElement tableAttribute;
                XElement indexes;

                table = GetTableElement(tableName);
                foreach (string attributeName in attributeNames)
                {
                    if (attributeName.Equals("") || attributeName == null)
                        throw new Exception("Invalid table attribute name! ");
                    tableAttribute = GetTableAttributeElement(table, attributeName);
                    keyLength += Int32.Parse(GetTableAttributeLengthAttribute(tableAttribute).Value);
                }
                indexes = GetIndexesElement(table);

                XElement index = new XElement(xmlns + indexElement);
                XAttribute indexFileNameIndexAttribute = new XAttribute(indexFileNameAttribute, indexFileName);
                XAttribute indexTypeIndexAttribute = new XAttribute(indexTypeAttribute, indexType);
                XAttribute isUniqueIndexAttribute = new XAttribute(isUniqueAttribute, isUnique);
                XAttribute keylengthIndexAttribute = new XAttribute(keyLengthAttribute, keyLength);
                XElement indexAttributes = new XElement(xmlns + indexAttributesElement);

                index.Add(indexFileNameIndexAttribute);
                index.Add(indexTypeIndexAttribute);
                index.Add(isUniqueIndexAttribute);
                index.Add(keylengthIndexAttribute);
                index.Add(indexAttributes);
                foreach (string attributeName in attributeNames)
                {
                    XElement indexAttribute = new XElement(xmlns + indexAttributeElement);
                    indexAttribute.Value = attributeName;
                    indexAttributes.Add(indexAttribute);
                }

                indexes.Add(index);
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                string attributes = "[ ";
                foreach (string attributeName in attributeNames)
                    attributes += attributeName + " ";
                attributes += "]";
                throw new Exception("Could not create index in table \"" + tableName + "\" on table attributes (columns) " + attributes +
                    " of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Drop an existing index from catalog
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="indexFileName">Name of the index file</param>
        public override void DropIndex(string tableName, string indexFileName)
        {
            try
            {
                if (tableName.Equals("") || tableName == null)
                    throw new Exception("Invalid table name! ");
                if (indexFileName.Equals("") || indexFileName == null)
                    throw new Exception("Invalid index file name! ");

                XElement table;
                XElement index;

                table = GetTableElement(tableName);
                index = GetIndexElementByFileName(table, indexFileName);

                index.Remove();
                SaveCatalog();
            }
            catch (Exception e)
            {
                LoadCatalog();
                throw new Exception("Could not drop index in table \"" + tableName + "\" with name " + indexFileName +
                    " of database \"" + databaseName + "\" in catalog! ", e);
            }
        }

        /// <summary>
        /// Check if table attribute has index
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="attributeName">Name of the attribute</param>
        /// <returns>True if is foreign key reference, else false</returns>
        public override bool HasIndex(string tableName, string attributeName)
        {
            XElement table;
            bool hasIndex = true;

            table = GetTableElement(tableName);
            try
            {
                int count = GetAllIndexElementsReferencingOnAttribute(table, attributeName).Count();
                if (count <= 0)
                    hasIndex = false;
            }
            catch (Exception)
            {
                hasIndex = false;
            }

            return hasIndex;
        }

        /// <summary>
        /// Get index filename
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="indexAttribute">Index attribute</param>
        /// <returns>Filename of the index</returns>
        public override string GetIndexFileName(string tableName, string indexAttribute)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement index = GetIndexElementByAttribute(table, indexAttribute);
                return GetIndexFileNameAttribute(index).Value;
            }
            catch (Exception e)
            {
                throw new Exception("Could not get index filename for index attribute \"" + indexAttribute +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get index attributes
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="indexFileName">Name of the index file</param>
        /// <returns>Index attributes</returns>
        public override List<string> GetIndexAttributes(string tableName, string indexFileName)
        {
            List<string> indexAttributes = new List<string>();
            try
            {
                XElement table = GetTableElement(tableName);
                XElement index = GetIndexElementByFileName(table, indexFileName);
                foreach (XElement indexAttribute in GetIndexAttributes(index))
                    indexAttributes.Add(indexAttribute.Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get index attributes for index file \"" + indexFileName +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
            return indexAttributes;
        }

        /// <summary>
        /// Get index type
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="indexFileName">Name of the index file</param>
        /// <returns>Type of index</returns>
        public override IndexType GetIndexType(string tableName, string indexFileName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement index = GetIndexElementByFileName(table, indexFileName);
                return (IndexType)Enum.Parse(typeof(IndexType), GetIndexTypeAttribute(index).Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get index type of index with filename \"" + indexFileName +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get index is unique
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="indexFileName">Name of the index file</param>
        /// <returns>True if index is unique, else false</returns>
        public override bool IsIndexUnique(string tableName, string indexFileName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement index = GetIndexElementByFileName(table, indexFileName);
                return Boolean.Parse(GetIndexIsUniqueAttribute(index).Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get if index is unique with filename \"" + indexFileName +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

        /// <summary>
        /// Get index key length attribute
        /// </summary>
        /// <param name="tableName">Name of the table</param>
        /// <param name="indexFileName">Name of the index file</param>
        /// <returns>Length of the index key</returns>
        public override int GetIndexKeyLength(string tableName, string indexFileName)
        {
            try
            {
                XElement table = GetTableElement(tableName);
                XElement index = GetIndexElementByFileName(table, indexFileName);
                return Int32.Parse(GetIndexKeyLengthAttribute(index).Value);
            }
            catch (Exception e)
            {
                throw new Exception("Could not get index type of index with filename \"" + indexFileName +
                    "\" in table \"" + tableName + "\" from catalog for database \"" + databaseName + "\"! ", e);
            }
        }

    }
}
